package com.bsj.power.pls.access;

import cn.hutool.core.util.HexUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.bsj.power.common.config.cached.BindCardCached;
import com.bsj.power.common.config.cached.PathAlarmCached;
import com.bsj.power.common.config.cached.PathCached;
import com.bsj.power.common.config.cached.RedisCached;
import com.bsj.power.common.config.redis.AlarmConstant;
import com.bsj.power.common.config.redis.RedisConstant;
import com.bsj.power.common.config.rocketmq.producer.BaseMqPushUtil;
import com.bsj.power.common.constant.MqTopicAndTagConstants;
import com.bsj.power.common.constant.NumericalConstants;
import com.bsj.power.common.constant.UWBPosConstant;
import com.bsj.power.common.def.dto.cardReport.BindCardDTO;
import com.bsj.power.common.def.entity.alarmReport.AlarmReport;
import com.bsj.power.common.def.entity.job.Job;
import com.bsj.power.common.def.entity.job.WorkPersonnel;
import com.bsj.power.common.def.entity.locationDevice.LocationDevice;
import com.bsj.power.common.def.entity.path.Path;
import com.bsj.power.common.def.entity.path.PathPoint;
import com.bsj.power.common.def.entity.pathReport.PathReport;
import com.bsj.power.common.def.entity.personGroup.BindGroupData;
import com.bsj.power.common.def.entity.personGroup.PersonGroup;
import com.bsj.power.common.def.entity.personnel.Personnel;
import com.bsj.power.common.def.enums.FileTypeEnum;
import com.bsj.power.common.def.handleData.path.MidPathAlarmState;
import com.bsj.power.common.def.handleData.path.MidPathNoticePerson;
import com.bsj.power.common.def.mq.sdk.SdkAnnex;
import com.bsj.power.common.def.vo.WebSocketMsgVO;
import com.bsj.power.common.def.vo.pls.httpVo.HttpTagPosVO;
import com.bsj.power.common.http.ExternalAccessHttp;
import com.bsj.power.common.mapper.JobPersonnelMapper;
import com.bsj.power.common.mapper.PersonnelMapper;
import com.bsj.power.common.util.DateUtils;
import com.bsj.power.common.util.FindMediaDevUtils;
import com.bsj.power.common.util.StringUtils;
import com.bsj.power.pls.config.cached.HighRiseCached;
import com.bsj.power.pls.config.net.netty.protocol.UWBAck;
import lombok.extern.slf4j.Slf4j;

import javax.validation.constraints.NotBlank;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author yinhao
 * @version 1.0
 * @description 进出围栏常用方法
 * @date 2024/9/15
 */
@Slf4j
public class PathUseUtil {

    private static final long ONE_DAY = 1000L * 60 * 60 * 24;


    /**
     * 判断该人员进入围栏是否需要报警
     *
     * @param bindGroupDataMap
     * @param empNo            人员工号
     * @param path             电子围栏信息
     * @param personType       人员类型 1:负责人 2:作业人员
     * @param roomJobMap       围栏下的所有敏感区域的工单数据
     * @param inPoly           true 在区域内  false 不在区域内
     * @return true 需要报警 false 不报警，但是记录进出记录
     */
    public static boolean authGroupInfo(Map<String, BindGroupData> bindGroupDataMap,
                                        String empNo, Path path, Integer personType,
                                        Map<String, String> roomJobMap, boolean inPoly,
                                        BindCardCached bindCardCached, RedisCached redisCached,
                                        String areaCoordinates) {
        Integer pathId = path.getPathId();
//        if (personType.equals(NumericalConstants.ONE)) {
//            //负责人进入围栏不进行围栏报警触发,电子围栏不进行任何报警
//            return false;
//        }
//        if (StringUtils.isNotEmpty(leadMapId)) {
//            //如果有负责人在此楼层，那所有人员都不产生报警
//            return false;
//        }
        if (roomJobMap != null) {
            Date nowTime = new Date();
            Iterator<Map.Entry<String, String>> iterator = roomJobMap.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, String> next = iterator.next();
                //工单ID
                String key = next.getKey();
                //工单信息
                String value = next.getValue();
                Job job = JSON.parseObject(value, Job.class);
                if (job == null) {
                    continue;
                }
                //工单负责人工号
                String jobEmpNo = job.getEmpNo();
                if (jobEmpNo == null) {
                    log.warn("该敏感区域作业工单负责人工号不存在: {}", value);
                    continue;
                }
                //工单作业人员工号集合
                List<String> temporaryEmpNos = job.getTemporaryEmpNos();
                //如果是负责人
                if (personType.equals(NumericalConstants.ONE)) {
                    //该负责人是在敏感区域的工单中,那负责人不需要报警
                    if (jobEmpNo.equalsIgnoreCase(empNo)) {
                        return false;
                    }
                } else {
                    //作业人员
                    if (temporaryEmpNos == null) {
                        continue;
                    }
                    //判断当前的作业人员是否进行当前工单
                    if (!temporaryEmpNos.contains(empNo)) {
                        continue;
                    }
                    //判断作业人员与负责人是否都在区域内,如果都在区域内则作业人员不报警
                    if (!inPoly) {
                        continue;
                    }
                    //获取负责人员的最新位置数据
                    //绑卡信息
                    BindCardDTO bindCardDTO = bindCardCached.hgetBindCardInfo(RedisConstant.BIND_CARD_REDIS_EMP_NO_KEY + jobEmpNo);
                    if (bindCardDTO == null) {
                        log.info("敏感区域判断时负责人未绑卡： {}", value);
                        continue;
                    }
                    String deviceName = bindCardDTO.getDeviceName();
                    //位置信息
                    String str = redisCached.get(UWBPosConstant.REDIS_TAG_POS_KEY + deviceName);
                    if (StringUtils.isEmpty(str)) {
                        continue;
                    }
                    HttpTagPosVO httpTagPosVO = JSONObject.parseObject(str, HttpTagPosVO.class);
                    if (httpTagPosVO == null) {
                        continue;
                    }
                    //定位时间
                    long voTime = httpTagPosVO.getTime();
                    if (nowTime.getTime() - voTime > 1000 * 60 * 15) {
                        //负责人位置点如果超过15分钟，代表已经离线
                        log.warn("负责人最新位置点已经超过15分钟: {}", JSON.toJSONString(httpTagPosVO));
                        continue;
                    }
                    //负责人最新位置点
                    double[] voPos = httpTagPosVO.getReducePos();
                    //负责人是否在区域中
                    boolean fzPoly = FindMediaDevUtils.IsPathMapInPoly(voPos[0], -voPos[1], areaCoordinates,
                            httpTagPosVO.getMapId(), path.getMapId());
                    if (fzPoly) {
                        //负责人在区域内，作业人员也在区域内,则该作业人员不产生报警
                        return false;
                    }
                }
            }
        }
        if (bindGroupDataMap == null) {
            return true;
        }
        BindGroupData bindGroupData = bindGroupDataMap.get(empNo);
        if (bindGroupData == null) {
            return true;
        }
        PersonGroup personGroup = bindGroupData.getPersonGroup();
        Date startTime = personGroup.getStartTime();
        Date endTime = personGroup.getEndTime();
        Date date = new Date();
        if (startTime != null && endTime != null) {
            //需要判断时间
            if (date.getTime() < startTime.getTime() || date.getTime() > endTime.getTime()) {
                return true;
            }
        }
        List<Integer> pathList = personGroup.getPathList();
        if (!pathList.contains(pathId)) {
            return true;
        }
        return false;
    }

    /**
     * 禁止驶入围栏
     *
     * @param midState          中间状态
     * @param sn                设备号
     * @param pathId            围栏ID
     * @param pathName          围栏名称
     * @param pathStatus        围栏类型（进/出）
     * @param inPoly            是否在区域范围内
     * @param personnelId       人员ID
     * @param personnelName     人员名称
     * @param personType        1负责人 2作业人员
     * @param nikeName          设备别名
     * @param deviceType        设备类型
     * @param time              定位时间
     * @param pos               定位位置点
     * @param redisCached
     * @param pathAlarmCached
     * @param midPathAlarmState
     * @param insertAlarmReport
     */
    public static void notEnterPath(int midState, String sn, Integer pathId, String pathName, String pathAddress, Integer pathStatus,
                                    boolean inPoly, Integer personnelId, String personnelName,
                                    String empNo, Integer personType,
                                    String nikeName, Integer deviceType, long time, double[] pos,
                                    RedisCached redisCached, PathAlarmCached pathAlarmCached,
                                    MidPathAlarmState midPathAlarmState, List<Long> cameraIds,
                                    List<AlarmReport> insertAlarmReport,
                                    BaseMqPushUtil baseMqPushUtil, String path, List<PathPoint> pathPoints,
                                    WorkPersonnel workPersonnel, JobPersonnelMapper jobPersonnelMapper,
                                    PersonnelMapper personnelMapper, Integer alarmLevel,
                                    Boolean alarmFlag, List<PathReport> insertPathReport,
                                    Boolean systemFlag, String ipHost, long id) {
        double x = pos[0];
        double y = pos[1];
        double z = pos[2];
        if (midState == 0) {
            //中间状态正常，本次不在区域内，不需要处理直接返回
            if (!inPoly) {
                redisCached.hset(AlarmConstant.MID_SAFETY_PATH_STATE + sn, pathId + "", JSON.toJSONString(midPathAlarmState));
            } else {
                //中间状态正常，本次在区域中，需要作报警处理
                AlarmReport alarmReport = new AlarmReport(sn, personnelId, pathId,
                        nikeName, deviceType, personnelName, pathName, pathStatus,
                        time, Arrays.toString(pos), 1, personType);
                alarmReport.setCameraIds(cameraIds);
                alarmReport.setPathAddress(pathAddress);
                alarmReport.setAlertLevel(alarmLevel);
                //todo begin
                Job job = null;
                Personnel directorInfo = null;
                //根据人员id查询最新的工单关联
                Integer jobId = jobPersonnelMapper.getLastJobIdByPersonnelId(personnelId);
                if (jobId != null) {
                    String jobStr = redisCached.hget(RedisConstant.JOB_ORDER_INFO_KEY, jobId + "");
                    if (StringUtils.isNotBlank(jobStr)) {
                        job = JSON.parseObject(jobStr, Job.class);
                        String directorId = job.getDirectorId();
                        if (StringUtils.isNotBlank(directorId)) {
                            //负责人信息
                            directorInfo = personnelMapper.selectOne(Wrappers.<Personnel>lambdaQuery().eq(Personnel::getDirectorId, directorId));
                        }
                    }
                }
                if (job != null && job.getStatus() != null && job.getStatus() != 2) {
                    //工单信息,已完成的不展示
                    alarmReport.setJob(job);
                }
                //负责人信息
                alarmReport.setDirectorInfo(directorInfo);
                //作业人员信息
                alarmReport.setWorkPersonnel(workPersonnel);
                // todo end
                //缓存报警状态
                pathAlarmCached.hsetPathAlarmInfo(alarmReport);
                // 修改围栏报警中间状态
                midPathAlarmState.setState(NumericalConstants.ONE);
                midPathAlarmState.setTime(time);
                midPathAlarmState.setX(x);
                midPathAlarmState.setY(y);
                midPathAlarmState.setZ(z);
                redisCached.hset(AlarmConstant.MID_SAFETY_PATH_STATE + sn, pathId + "", JSON.toJSONString(midPathAlarmState));

                //人员进出都需要记录
                PathReport pathReport = new PathReport();
                pathReport.setDeviceName(sn);
                pathReport.setPathId(pathId);
                pathReport.setPathName(pathName);
                pathReport.setPersonnelId(personnelId);
                pathReport.setPersonnelType(personType);
                pathReport.setEmpNo(empNo);
                pathReport.setType(NumericalConstants.ONE);
                pathReport.setCreateTime(new Date(time));
                insertPathReport.add(pathReport);
                if (!alarmFlag) {
                    //todo 进入之后负责人需要进行通知
                    if (personType.equals(NumericalConstants.ONE)) {
                        WebSocketMsgVO webSocketMsgVO = WebSocketMsgVO.builder()
                                .msgType(NumericalConstants.FIVE)
                                .obj(alarmReport)
                                .build();
                        baseMqPushUtil.sendTopicBytes(MqTopicAndTagConstants.SYSTEM_WEB_SERVER_CHANGE_TOPIC,
                                MqTopicAndTagConstants.SYSTEM_WEB_PRODUCT_CHANGE_TAG, JSON.toJSONBytes(webSocketMsgVO));
                    }
                } else {
                    //1.报警附件创建
                    annexFileAdd(new Date(time), cameraIds, path, baseMqPushUtil, alarmReport, false);
                    //2.报警信息推送
                    WebSocketMsgVO webSocketMsgVO = WebSocketMsgVO.builder()
                            .msgType(NumericalConstants.TWO)
                            .obj(alarmReport)
                            .build();
                    baseMqPushUtil.sendTopicBytes(MqTopicAndTagConstants.SYSTEM_WEB_SERVER_CHANGE_TOPIC,
                            MqTopicAndTagConstants.SYSTEM_WEB_PRODUCT_CHANGE_TAG, JSON.toJSONBytes(webSocketMsgVO));
                    //3.产生报警时也需要给对应设备下发声光报警,响5次
                    byte[] alarmCmd = UWBAck.alarmCmd(new byte[]{0x05, 0x01});
                    if (systemFlag) {
                        ExternalAccessHttp.transmitDataHttpPut("A", ipHost, id + "", HexUtil.encodeHexStr(alarmCmd));
                    }
                }
            }
        } else if (midState == 1) {
            //中间状态不正常, 本次不在区域里,需要结束报警并进行数据记录，并修改中间状态
            if (!inPoly) {
                AlarmReport alarmReport = new AlarmReport(sn, personnelId, pathId,
                        nikeName, deviceType, personnelName, pathName, pathStatus,
                        time, Arrays.toString(pos), 1, personType);
                Date beginTime = new Date(midPathAlarmState.getTime());
                Date endTime = new Date(time);
                if (time - beginTime.getTime() < 1000L) {
                    //小于1秒
                    endTime = new Date(time + 2000L);
                } else if (System.currentTimeMillis() - beginTime.getTime() > ONE_DAY) {
                    // 超过1天的报警
                    endTime = new Date(midPathAlarmState.getTime() + 15000L);
                }
                alarmReport.setBeginTime(beginTime);
                alarmReport.setEndTime(endTime);
                //开始与结束的位置点
                alarmReport.setPos(Arrays.toString(new Double[]{midPathAlarmState.getX(), midPathAlarmState.getY(), midPathAlarmState.getZ()}));
                alarmReport.setEndPos(Arrays.toString(pos));

                //人员进出都需要记录
                PathReport pathReport = new PathReport();
                pathReport.setDeviceName(sn);
                pathReport.setPathId(pathId);
                pathReport.setPathName(pathName);
                pathReport.setPersonnelId(personnelId);
                pathReport.setPersonnelType(personType);
                pathReport.setEmpNo(empNo);
                pathReport.setType(NumericalConstants.TWO);
                pathReport.setCreateTime(new Date(time));
                insertPathReport.add(pathReport);
                if (!alarmFlag) {

                } else {
                    //报警附件创建
                    annexFileAdd(beginTime, cameraIds, path, baseMqPushUtil, alarmReport, true);
                    insertAlarmReport.add(alarmReport);
                    //发送消息通知组合报警
                    baseMqPushUtil.sendTopicBytes(MqTopicAndTagConstants.BASE_SERVER_PLS_CHANGE_TOPIC,
                            MqTopicAndTagConstants.BASE_CHANGE_PLS_UPDATE_TAG, JSON.toJSONBytes(alarmReport));
                }
                //恢复中间位置状态
                midPathAlarmState.setState(NumericalConstants.ZERO);
                midPathAlarmState.setTime(time);
                midPathAlarmState.setX(x);
                midPathAlarmState.setY(y);
                midPathAlarmState.setZ(z);
                redisCached.hset(AlarmConstant.MID_SAFETY_PATH_STATE + sn, pathId + "", JSON.toJSONString(midPathAlarmState));
                //删除缓存中的报警数据
                pathAlarmCached.hdelPathAlarmInfo(sn, pathId);
            } else {
                //中间状态不正常，本次在区域内，不做处理
            }
        }
    }

    /**
     * 禁止驶出围栏
     *
     * @param midState
     * @param sn
     * @param pathId
     * @param pathName
     * @param pathStatus
     * @param inPoly
     * @param personnelId
     * @param personnelName
     * @param nikeName
     * @param deviceType
     * @param time
     * @param pos
     * @param redisCached
     * @param pathAlarmCached
     * @param midPathAlarmState
     * @param insertAlarmReport
     */
    public static void notDriveOutPath(int midState, String sn, Integer pathId, String pathName, Integer pathStatus,
                                       boolean inPoly, Integer personnelId, String personnelName, String empNo,
                                       Integer personType, String nikeName,
                                       Integer deviceType, long time, double[] pos, RedisCached redisCached,
                                       PathAlarmCached pathAlarmCached, MidPathAlarmState midPathAlarmState,
                                       List<Long> cameraIds, List<AlarmReport> insertAlarmReport,
                                       BaseMqPushUtil baseMqPushUtil, String path, WorkPersonnel workPersonnel,
                                       JobPersonnelMapper jobPersonnelMapper, PersonnelMapper personnelMapper,
                                       Integer alarmLevel, Boolean alarmFlag, List<PathReport> insertPathReport) {
        double x = pos[0];
        double y = pos[1];
        double z = pos[2];
        //禁止出围栏，对应出围栏报警
        if (midState == 0) {
            //中间状态正常
            if (inPoly) {
                //本次在围栏内，不需要处理
                redisCached.hset(AlarmConstant.MID_SAFETY_PATH_STATE + sn, pathId + "", JSON.toJSONString(midPathAlarmState));
            } else {
                //中间状态正常，本次不在围栏区域中，需要报警
                AlarmReport alarmReport = new AlarmReport(sn, personnelId, pathId,
                        nikeName, deviceType, personnelName, pathName, pathStatus,
                        time, Arrays.toString(pos), 1, personType);
                alarmReport.setCameraIds(cameraIds);
                alarmReport.setAlertLevel(alarmLevel);
                //todo begin
                Job job = null;
                Personnel directorInfo = null;
                //根据人员id查询最新的工单关联
                Integer jobId = jobPersonnelMapper.getLastJobIdByPersonnelId(personnelId);
                if (jobId != null) {
                    String jobStr = redisCached.hget(RedisConstant.JOB_ORDER_INFO_KEY, jobId + "");
                    if (StringUtils.isNotBlank(jobStr)) {
                        job = JSON.parseObject(jobStr, Job.class);
                        String directorId = job.getDirectorId();
                        if (StringUtils.isNotBlank(directorId)) {
                            //负责人信息
                            directorInfo = personnelMapper.selectOne(Wrappers.<Personnel>lambdaQuery().eq(Personnel::getDirectorId, directorId));
                        }
                    }
                }
                //工单信息
                alarmReport.setJob(job);
                //负责人信息
                alarmReport.setDirectorInfo(directorInfo);
                //作业人员信息
                alarmReport.setWorkPersonnel(workPersonnel);
                // todo end
                //缓存报警状态
                pathAlarmCached.hsetPathAlarmInfo(alarmReport);
                // 修改围栏报警中间状态
                midPathAlarmState.setState(NumericalConstants.ONE);
                midPathAlarmState.setTime(time);
                midPathAlarmState.setX(x);
                midPathAlarmState.setY(y);
                midPathAlarmState.setZ(z);
                redisCached.hset(AlarmConstant.MID_SAFETY_PATH_STATE + sn, pathId + "", JSON.toJSONString(midPathAlarmState));

                PathReport pathReport = new PathReport();
                pathReport.setDeviceName(sn);
                pathReport.setPathId(pathId);
                pathReport.setPathName(pathName);
                pathReport.setPersonnelId(personnelId);
                pathReport.setPersonnelType(personType);
                pathReport.setEmpNo(empNo);
                pathReport.setType(NumericalConstants.TWO);
                pathReport.setCreateTime(new Date(time));
                insertPathReport.add(pathReport);
                if (!alarmFlag) {

                } else {
                    //报警附件创建
                    annexFileAdd(new Date(time), cameraIds, path, baseMqPushUtil, alarmReport, false);

                    //报警信息推送
                    WebSocketMsgVO webSocketMsgVO = WebSocketMsgVO.builder()
                            .msgType(NumericalConstants.TWO)
                            .obj(alarmReport)
                            .build();
                    baseMqPushUtil.sendTopicBytes(MqTopicAndTagConstants.SYSTEM_WEB_SERVER_CHANGE_TOPIC,
                            MqTopicAndTagConstants.SYSTEM_WEB_PRODUCT_CHANGE_TAG, JSON.toJSONBytes(webSocketMsgVO));
                }
            }
        } else if (midState == 1) {
            //中间状态不正常,在围栏区域内,需要结束报警并保存数据，并修改中间状态
            if (inPoly) {
                AlarmReport alarmReport = new AlarmReport(sn, personnelId, pathId,
                        nikeName, deviceType, personnelName, pathName, pathStatus,
                        time, Arrays.toString(pos), 1, personType);
                Date beginTime = new Date(midPathAlarmState.getTime());
                Date endTime = new Date(time);
                if (time - beginTime.getTime() < 1000L) {
                    // 小于1秒
                    endTime = new Date(time + 2000L);
                } else if (System.currentTimeMillis() - beginTime.getTime() > ONE_DAY) {
                    // 超过1天的报警
                    endTime = new Date(midPathAlarmState.getTime() + 15000L);
                }
                alarmReport.setBeginTime(beginTime);
                alarmReport.setEndTime(endTime);
                //开始与结束的位置点
                alarmReport.setPos(Arrays.toString(new Double[]{midPathAlarmState.getX(), midPathAlarmState.getY(), midPathAlarmState.getZ()}));
                alarmReport.setEndPos(Arrays.toString(pos));
                //记录围栏的进出记录
                PathReport pathReport = new PathReport();
                pathReport.setDeviceName(sn);
                pathReport.setPathId(pathId);
                pathReport.setPathName(pathName);
                pathReport.setPersonnelId(personnelId);
                pathReport.setPersonnelType(personType);
                pathReport.setEmpNo(empNo);
                pathReport.setType(NumericalConstants.ONE);
                pathReport.setCreateTime(new Date(time));
                insertPathReport.add(pathReport);
                if (!alarmFlag) {

                } else {
                    //报警附件创建
                    annexFileAdd(beginTime, cameraIds, path, baseMqPushUtil, alarmReport, true);
                    insertAlarmReport.add(alarmReport);
                    //发送消息通知组合报警
                    baseMqPushUtil.sendTopicBytes(MqTopicAndTagConstants.BASE_SERVER_PLS_CHANGE_TOPIC,
                            MqTopicAndTagConstants.BASE_CHANGE_PLS_UPDATE_TAG, JSON.toJSONBytes(alarmReport));
                }
                //恢复中间位置状态
                midPathAlarmState.setState(NumericalConstants.ZERO);
                midPathAlarmState.setTime(time);
                midPathAlarmState.setX(x);
                midPathAlarmState.setY(y);
                midPathAlarmState.setZ(z);
                redisCached.hset(AlarmConstant.MID_SAFETY_PATH_STATE + sn, pathId + "", JSON.toJSONString(midPathAlarmState));
                //删除缓存中的报警数据
                pathAlarmCached.hdelPathAlarmInfo(sn, pathId);
            }
        }
    }

    /**
     * 工作人员进入围栏进行通知
     *
     * @param paths
     * @param pathCached
     * @param sn
     * @param aX
     * @param aZ
     * @param redisCached
     * @param personnel
     * @param time
     * @param pos
     * @param baseMqPushUtil
     * @param locationDevice
     */
    public static void noticeFormPerson(List<Path> paths, PathCached pathCached, String sn,
                                        double aX, double aZ, RedisCached redisCached, Personnel personnel,
                                        long time, double[] pos, BaseMqPushUtil baseMqPushUtil,
                                        LocationDevice locationDevice, Integer posSpace, int mapId) {
        for (Path path : paths) {
            try {
                //围栏ID
                Integer pathId = path.getPathId();
                //围栏名称
                String pathName = path.getPathName();
                Integer pathType = path.getPathType();
                // 围栏地址
                String pathAddress = path.getPathAddress();
                if (StringUtils.isEmpty(pathAddress)) {
                    pathAddress = pathName;
                }
                //围栏空间坐标
                Integer pathMapId = path.getMapId();
                if (pathMapId == null) {
                    //围栏信息错误
                    log.info("负责人停留围栏机组坐标ID不对，无效mapId: {}", pathMapId);
                    continue;
                }
                //需要确认空间是否一致
                //围栏的机组信息
                Integer pathSpace = HighRiseCached.getSpace(pathMapId);
                if (!posSpace.equals(pathSpace)) {
                    log.info("围栏机组坐标与设备机组不匹配，设备mapId: {}, 围栏mapId:{}", mapId, pathMapId);
                    continue;
                }
                //关联摄像头信息
                List<Long> cameraIds = pathCached.smemsPathDevice(pathId);
                //判断是否在该围栏内
                List<PathPoint> pathPoints = path.getPathPoints();
                List<String> xyAddList = pathPoints.stream().map(PathPoint::toAddString).collect(Collectors.toList());
                String areaCoordinates = xyAddList.stream().collect(Collectors.joining("|"));
//                boolean inPoly = FindMediaDevUtils.IsPtInPoly(aX, aZ, areaCoordinates);
                boolean inPoly = FindMediaDevUtils.IsPathMapInPoly(aX, aZ, areaCoordinates, mapId, pathMapId);
                log.info("负责人通知围栏信息: {}, X: {}, Z: {}, 对应的位置点信息: {}, 是否在区域内: {}",
                        pathName, aX, aZ, areaCoordinates, inPoly);
                if (!inPoly) {
                    //不在范围里，直接跳过
                    continue;
                }
                //根据设备号跟围栏ID获取中间状态
                String midNoticeState = redisCached.hget(AlarmConstant.MID_NOTICE_PERSON_PATH_STATE + sn, pathId + "");
                //围栏报警中间状态
                MidPathNoticePerson midPathNoticePerson;
                Boolean fistFlag = false;
                if (midNoticeState == null) {
                    //第一次进入
                    midPathNoticePerson = MidPathNoticePerson.builder()
                            .time(time).x(pos[0]).y(pos[1]).z(pos[2]).build();
                    fistFlag = true;
                } else {
                    midPathNoticePerson = JSON.parseObject(midNoticeState, MidPathNoticePerson.class);
                }
                long personTime = midPathNoticePerson.getTime();
                //第一次进入直接通知，后续再根据上次通知的时间来判断
                if (fistFlag || time - personTime > 10 * 1000) {
                    //现在设置为10秒通知一次
                    midPathNoticePerson = MidPathNoticePerson.builder()
                            .time(time).x(pos[0]).y(pos[1]).z(pos[2]).build();
                    //更新通知时间
                    redisCached.hset(AlarmConstant.MID_NOTICE_PERSON_PATH_STATE + sn, pathId + "", JSON.toJSONString(midPathNoticePerson));
                    //报警信息推送
                    AlarmReport alarmReport = new AlarmReport(sn, personnel.getPersonnelId(), pathId,
                            locationDevice.getNikeName(), locationDevice.getDeviceType(), personnel.getName(),
                            pathName, 0, time, Arrays.toString(pos), 1, 1);
                    alarmReport.setCameraIds(cameraIds);
                    alarmReport.setPathAddress(pathAddress);
//                    String jobStr = redisCached.hget(RedisConstant.JOB_ORDER_INFO_KEY, jobId + "");
//                    if (StringUtils.isNotBlank(jobStr)) {
//                        alarmReport.setJob(JSON.parseObject(jobStr, Job.class));
//                    }
                    WebSocketMsgVO webSocketMsgVO = WebSocketMsgVO.builder()
                            .msgType(NumericalConstants.FIVE)
                            .obj(alarmReport)
                            .build();
                    baseMqPushUtil.sendTopicBytes(MqTopicAndTagConstants.SYSTEM_WEB_SERVER_CHANGE_TOPIC,
                            MqTopicAndTagConstants.SYSTEM_WEB_PRODUCT_CHANGE_TAG, JSON.toJSONBytes(webSocketMsgVO));

                }
            } catch (Exception e) {
                log.error("负责人进入围栏判断错误");
            }
        }
    }

    /**
     * 报警附件创建
     *
     * @param beginTime
     * @param cameraIds
     * @param path
     * @param baseMqPushUtil
     * @param alarmReport
     */
    private static void annexFileAdd(Date beginTime, List<Long> cameraIds, String path,
                                     BaseMqPushUtil baseMqPushUtil, AlarmReport alarmReport,
                                     boolean sendFlag) {
        long first = beginTime.getTime() - 30000;
        String firstHalfMinute = DateUtils.dateToStr(new Date(first));
        String endMinute = DateUtils.dateToStr(beginTime);
        List<String> pathList = new ArrayList<>();
        cameraIds.stream().forEach(item -> {
            //采用异步创建报警附件的方式
            String filePath = filePath(beginTime, path,
                    FileTypeEnum.UWB_ALARM.getFileType(), item);
            pathList.add(filePath);
            SdkAnnex sdkAnnex = SdkAnnex.builder()
                    .devId(item).startTime(firstHalfMinute)
                    .endTime(endMinute).path(filePath).build();
            if (sendFlag) {
                //气泡框报警的时候不发送创建附件，报警结束后才创建
                baseMqPushUtil.sendTopicBytes(MqTopicAndTagConstants.SDK_FILE_SERVER_TOPIC,
                        MqTopicAndTagConstants.SDK_FILE_DOWNLOAD_TAG, JSON.toJSONBytes(sdkAnnex));
            }
        });
        if (!pathList.isEmpty()) {
            alarmReport.setFilePath(pathList.stream().collect(Collectors.joining(",")));
        }
    }

    /**
     * 生成对应的文件路径
     *
     * @param time     时间
     * @param path     文件存储路径
     * @param fileType 附件类型
     * @return
     */
    private static String filePath(Date time, String path, Integer fileType, Long devId) {
        // 日期格式
        String date = DateUtils.dateToStr(DateUtils.TIME_STANDARD_FORMAT_YMD, time);
        // 文件存储路径
        String filePath = path;
        // 文件类型存储路径
        String fileTypePath = "/" + FileTypeEnum.getByValue(fileType) + "/"
                + date + "/" + devId + "/";
        File folder = new File(filePath + fileTypePath);
        if (!folder.exists()) {
            folder.mkdirs();
        }
        // 文件名
        String fileName = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(time) + ".mp4";
        return fileTypePath + fileName;
    }
}
